package com.sh.rbac.core.encrypt.encryptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import com.sh.rbac.core.encrypt.annotation.FieldEncrypt;
import com.sh.rbac.core.encrypt.enums.AlgorithmType;
import com.sh.rbac.core.encrypt.enums.EncodeType;
import com.sh.rbac.core.encrypt.properties.EncryptProperties;
import com.sh.rbac.core.encrypt.properties.FieldEncryptProperties;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 加密管理器
 *
 * @author wb
 * @since 2025-10-24
 */
@Slf4j
public class EncryptManager {

    /**
     * 缓存加密器
     */
    private static final Map<Integer, IEncryptor> ENCRYPTORS = new ConcurrentHashMap<>();

    /**
     * 缓存字段信息
     */
    private static final Map<Class<?>, Set<Field>> FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 处理对象的加解密
     *
     * @param obj              对象
     * @param encryptProperties 加密配置
     * @param isEncrypt        true为加密，false为解密
     */
    public static void processEncryption(Object obj, EncryptProperties encryptProperties, boolean isEncrypt)
            throws IllegalAccessException {

        FieldEncryptProperties fieldEncrypt = encryptProperties.getFieldEncrypt();

        // 禁用字段加密，直接返回
        if (!fieldEncrypt.isEnable()) {
            return;
        }

        if (obj == null) {
            return;
        }

        // 处理集合类型
        if (obj instanceof Collection<?> collection) {
            for (Object item : collection) {
                processEncryption(item, encryptProperties, isEncrypt);
            }
            return;
        }

        Class<?> clazz = obj.getClass();
        Set<Field> encryptedFields = getEncryptedFields(clazz);

        if (CollUtil.isEmpty(encryptedFields)) {
            return;
        }

        for (Field field : encryptedFields) {
            field.setAccessible(true);
            Object value = field.get(obj);
            if (value instanceof String) {
                processFieldEncryption(field, obj, (String) value, fieldEncrypt, isEncrypt);
            } else if (value != null && !isPrimitiveOrWrapper(value.getClass())) {
                // 递归处理嵌套对象
                processEncryption(value, encryptProperties, isEncrypt);
            }
        }
    }

    /**
     * 获取类中带有 @FieldEncrypt 注解的字段
     *
     * @param clazz 类
     * @return 带有注解的字段集合
     */
    private static Set<Field> getEncryptedFields(Class<?> clazz) {
        return FIELD_CACHE.computeIfAbsent(clazz, cls -> {
            Field[] fields = cls.getDeclaredFields();
            Set<Field> encryptedFields = new HashSet<>();
            for (Field field : fields) {
                if (field.isAnnotationPresent(FieldEncrypt.class)) {
                    encryptedFields.add(field);
                }
            }
            return encryptedFields;
        });
    }

    /**
     * 处理单个字段的加解密
     *
     * @param field         字段
     * @param obj           对象
     * @param value         字段值
     * @param fieldEncrypt  字段加密配置
     * @param isEncrypt     是否为加密操作
     */
    private static void processFieldEncryption(Field field, Object obj, String value,
                                               FieldEncryptProperties fieldEncrypt, boolean isEncrypt) {
        FieldEncrypt annotation = field.getAnnotation(FieldEncrypt.class);

        // 获取字段的加密参数
        AlgorithmType algorithm = annotation.algorithm() == AlgorithmType.DEFAULT ?
                fieldEncrypt.getAlgorithm() : annotation.algorithm();
        String secretKey = annotation.secretKey().isEmpty() ?
                fieldEncrypt.getSecretKey() : annotation.secretKey();
        String privateKey = annotation.privateKey().isEmpty() ?
                fieldEncrypt.getPrivateKey() : annotation.privateKey();
        String publicKey = annotation.publicKey().isEmpty() ?
                fieldEncrypt.getPublicKey() : annotation.publicKey();
        EncodeType encode = annotation.encode() == EncodeType.DEFAULT ?
                fieldEncrypt.getEncode() : annotation.encode();

        // 创建加密上下文
        EncryptContext encryptContext = EncryptContext.builder()
                .algorithmType(algorithm)
                .encodeType(encode)
                .secretKey(secretKey)
                .privateKey(privateKey)
                .publicKey(publicKey)
                .value(value)
                .build();

        IEncryptor iEncryptor = getEncryptor(encryptContext);

        // 处理字段加密
        if (iEncryptor != null) {
            try {
                String processedValue = isEncrypt ?
                        iEncryptor.encrypt(encryptContext) :
                        iEncryptor.decrypt(encryptContext);
                field.set(obj, processedValue);
                log.debug("{}字段{}完成: {}", isEncrypt ? "加密" : "解密", field.getName(), processedValue);
            } catch (Exception e) {
                log.error("{}处理失败，字段: {}, 值: {}", isEncrypt ? "加密" : "解密", field.getName(), value, e);
            }
        }
    }

    /**
     * 获取加密器
     *
     * @param context 加密参数
     * @return 加密器
     */
    private static IEncryptor getEncryptor(EncryptContext context) {
        int key = context.hashCode();

        // 缓存中存在
        if (ENCRYPTORS.containsKey(key)) {
            return ENCRYPTORS.get(key);
        }

        // 创建加密器
        try {
            IEncryptor iEncryptor = ReflectUtil.newInstance(context.getAlgorithmType().getClazz());
            ENCRYPTORS.putIfAbsent(key, iEncryptor);
            return iEncryptor;
        } catch (Exception e) {
            log.error("创建加密器失败: {}", context.getAlgorithmType(), e);
            return null;
        }
    }

    /**
     * 判断是否为基础类型或包装类型
     *
     * @param clazz 类
     * @return 是否为基础类型
     */
    private static boolean isPrimitiveOrWrapper(Class<?> clazz) {
        return clazz.isPrimitive() ||
                clazz == String.class ||
                Number.class.isAssignableFrom(clazz) ||
                clazz == Boolean.class ||
                clazz == Character.class ||
                clazz.isEnum();
    }
}
